﻿using Dyao.Repository.Abstractions;
using Dyao.Book.Application.Abstractions;
using Dyao.Book.Application.TaskCenter;
using Dyao.Book.Application.TaskCenter.Manager;
using Dyao.Book.Contract;
using Dyao.Book.Core;
using Dyao.Book.Entity;
using Foundation.Core;
using Foundation.Core.ObjectMapping;
using Foundation.DistributeLock;
using Foundation.Uow;

namespace Dyao.Book.Application
{
    /// <summary>
    /// 任务中心执行管理器
    /// </summary>
    public class TaskCenterManager : ITaskCenterManagerIniter, ITransientDependency
    {
        private const int MAXTRYLOCKCOUNT = 3;
        #region 字段/属性

        private IDistributeLocker _distributeLocker;
        private IUnitOfWorkManager _unitOfWorkManager;
        private IJsonSerializer _jsonSerializer;

        private ITaskCenterRecordRepository _taskCenterRecordRepository;
        private ITaskCenterRecordExtensionRepository _taskCenterRecordExtensionRepository;
        private ITaskCenterRecordLogApplication _logService;
        private IObjectMapper _objectMapper;

        private TaskCenterRecordEntity _entity;
        private ExtensionInfoLocal _extension;
        private string _requestParameter = null;

        protected IServiceProvider ServiceProvider { get; set; }
        protected IDistributeLocker DistributeLocker => ServiceProvider.GetRequiredServiceReference(ref _distributeLocker);
        protected IUnitOfWorkManager UnitOfWorkManager => ServiceProvider.GetRequiredServiceReference(ref _unitOfWorkManager);
        protected IJsonSerializer JsonSerializer => ServiceProvider.GetRequiredServiceReference(ref _jsonSerializer);

        protected ITaskCenterRecordRepository RecordRepository => ServiceProvider.GetRequiredServiceReference(ref _taskCenterRecordRepository);
        protected ITaskCenterRecordExtensionRepository ExtensionRepository => ServiceProvider.GetRequiredServiceReference(ref _taskCenterRecordExtensionRepository);
        protected ITaskCenterRecordLogApplication LogService => ServiceProvider.GetRequiredServiceReference(ref _logService);

        protected IObjectMapper ObjectMapper => ServiceProvider.GetRequiredServiceReference(ref _objectMapper);

        public int TaskCenterRecordId { get; protected set; }

        public int CurrentRetryCount { get; protected set; }

        /// <summary>
        /// 是否分布式任务（任务是否有主任务和子任务）
        /// </summary>
        public bool IsDistributedTask { get; protected set; }

        public CancellationToken CancellationToken { get; protected set; }

        #endregion

        public TaskCenterManager(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null) throw new ArgumentNullException(nameof(serviceProvider));
            ServiceProvider = serviceProvider;
        }

        /// <summary>
        /// 开始任务
        /// </summary>
        /// <returns></returns>
        public virtual Task StartAsync()
        {
            return UpdateCoreAsync(async (context) =>
            {
                UpdateStartInfo(context);

                var model = await ExtensionRepository.GetByPropertyNameAsync(TaskCenterRecordId, new string[] { nameof(TaskCenterRecordExtensionEntity.Params) }, cancellationToken: CancellationToken);

                _requestParameter = model?.Params ?? "";
            },
            TaskCenterRecordStatusEnum.Pending,
            isLogComplete: false,
            logLevel: TaskCenterRecordLogTypeEnum.Business,
            logMessage: "任务开始执行");
        }

        /// <summary>
        /// 给任务添加进度
        /// </summary>
        /// <param name="progress"></param>
        /// <param name="isAutoComplete">是否自动推断任务是否已完成</param>
        /// <returns></returns>
        public Task AddProgressAsync(decimal progress, bool isAutoComplete = true)
        {
            return UpdateCoreAsync(context =>
            {
                context.ExcutingProgress = Math.Round(context.ExcutingProgress + progress, 2);
                if (isAutoComplete && context.ExcutingProgress >= 100)
                {
                    context.ExcuteEndDatetime = DateTime.Now;
                    context.Status = TaskCenterRecordStatusEnum.ExcuteSuccess;
                }
            },
            isLogComplete: isAutoComplete,
            logLevel: TaskCenterRecordLogTypeEnum.System,
            logFormatter: (context) => $"增加进度{progress:f2}%，任务进度{context.ExcutingProgress:f2}%");
        }

        /// <summary>
        /// 更新任务信息
        /// </summary>
        /// <param name="action">任务信息操作</param>
        /// <param name="isAutoComplete">是否自动推断任务是否已完成</param>
        /// <returns></returns>
        public Task UpdateProgressAsync(Action<TaskCenterRecordProgressModel> action, bool isAutoComplete = true)
        {
            return UpdateCoreAsync(context =>
            {
                var model = LoadModel(context, new TaskCenterRecordProgressModel());

                action(model);

                StoreContext(context, model, isAutoComplete);
            },
            isLogComplete: isAutoComplete,
            logLevel: TaskCenterRecordLogTypeEnum.System,
            logFormatter: (context) => $"更新任务信息，任务进度{context.ExcutingProgress:f2}%");
        }

        /// <summary>
        /// 执行成功
        /// </summary>
        /// <param name="resultMessage">执行成功的消息</param>
        /// <returns></returns>
        public Task SuccessAsync(string resultMessage = null)
        {
            return UpdateCoreAsync(context =>
            {
                context.ResultMessage = resultMessage;
                context.ExcutingProgress = 100;
                context.ExcuteEndDatetime = DateTime.Now;
                context.Status = TaskCenterRecordStatusEnum.ExcuteSuccess;
            }, isLogComplete: true);
        }

        /// <summary>
        /// 执行失败
        /// </summary>
        /// <param name="ex">异常</param>
        /// <param name="resultMassage">执行失败的消息</param>
        /// <returns></returns>
        public virtual async Task FailAsync(Exception ex = null, string resultMassage = null)
        {
            if (ex != null && ex is InternalTaskCenterException tcException)
            {
                await WriteLogAsync(TaskCenterRecordLogTypeEnum.System, tcException.Message, tcException.ToString(), tcException.ErrorCode);
                return;
            }

            if (string.IsNullOrWhiteSpace(resultMassage))
            {
                resultMassage = ex != null ? $"任务执行失败，错误消息：{ex.Message}" : "任务执行失败!";
            }

            await UpdateCoreAsync(context =>
            {
                context.ExcuteEndDatetime = DateTime.Now;
                context.Status = TaskCenterRecordStatusEnum.ExcuteFail;
                context.ResultMessage = resultMassage;
            }, isLogComplete: false);

            var content = ex == null ? null : $"异常信息：{ex}";

            await WriteLogAsync(TaskCenterRecordLogTypeEnum.Business, "任务执行失败!", content);
        }

        /// <summary>
        /// 获取任务的请求参数
        /// </summary>
        /// <returns></returns>
        public virtual async Task<string> GetRequestParamsAsync()
        {
            if (_requestParameter == null)
            {
                using (var unitOfwork = UnitOfWorkManager.Begin(true))
                {
                    var model = await ExtensionRepository.GetByPropertyNameAsync(TaskCenterRecordId, new string[] { nameof(TaskCenterRecordExtensionEntity.Params) }, cancellationToken: CancellationToken);
                    _requestParameter = model?.Params ?? "";
                }
            }

            return _requestParameter;
        }

        void ITaskCenterManagerIniter.Init(TaskCenterDto model, bool isDistributedTask, CancellationToken cancellationToken)
        {
            if (model.TaskCenterRecordId == 0) throw new ArgumentException("任务id不能为0");
            TaskCenterRecordId = model.TaskCenterRecordId;
            CurrentRetryCount = model.CurrentRetryCount;
            IsDistributedTask = isDistributedTask;

            CancellationToken = cancellationToken;
        }

        #region protected methods

        /// <summary>
        /// 更新任务，并记录更新日志。
        /// 如果<paramref name="logMessage"/>或者<paramref name="logFormatter"/>其中一个有值，则会记录更新日志，
        /// 如果<paramref name="isLogComplete"/>为true，则会记录任务完成日志(如果任务已完成)。
        /// </summary>
        /// <param name="action">更新操作</param>
        /// <param name="validateStatus">当前任务状态，用于验证</param>
        /// <param name="isLogComplete">是否记录完成信息(如果任务状态为成功或者失败)</param>
        /// <param name="logLevel">日志级别</param>
        /// <param name="logMessage">日志</param>
        /// <param name="logFormatter">日志格式化器</param>
        /// <returns>信息是否有变化</returns>
        protected virtual async Task<bool> UpdateCoreAsync(Action<TaskCenterUpdatingContext> action,
            TaskCenterRecordStatusEnum validateStatus = TaskCenterRecordStatusEnum.Excuting,
            bool isLogComplete = true,
            TaskCenterRecordLogTypeEnum logLevel = TaskCenterRecordLogTypeEnum.Business,
            string logMessage = null,
            Func<TaskCenterUpdatingContext, string> logFormatter = null)
        {
            CancellationToken.ThrowIfCancellationRequested();

            var hasChanges = false;//任务信息是否有改变
            TaskCenterUpdatingContext context = null;
            await UsingBlockingLockAsync(async () =>
            {
                using (var unitOfWork = UnitOfWorkManager.Begin(true))
                {
                    context = await LoadUpdatingContextAsync();
                    Validate(context, validateStatus);

                    action(context);

                    if (context.HasChanges())
                    {
                        await UpdateRecordAsync(context); //更新任务信息
                    }

                    if (context.ExtensionHasChanges())
                    {
                        await UpdateExtensionAsync(context); //更新扩展信息
                    }

                    hasChanges = context.HasChanges() || context.ExtensionHasChanges();
                    if (hasChanges)
                    {
                        await unitOfWork.CompleteAsync(CancellationToken);

                        if (!IsDistributedTask)
                        {
                            _entity = context.Record;
                            _extension = (context as IExtensionGetter)?.Get() ?? _extension;
                        }
                    }

                }
            });

            await WriteLogAsync(context, hasChanges, logLevel, logMessage, logFormatter, isLogComplete);

            return hasChanges;
        }

        /// <summary>
        /// 更新任务，并记录更新日志。
        /// 如果<paramref name="logMessage"/>或者<paramref name="logFormatter"/>其中一个有值，则会记录更新日志，
        /// 如果<paramref name="isLogComplete"/>为true，则会记录任务完成日志(如果任务已完成)。
        /// </summary>
        /// <param name="func">更新操作</param>
        /// <param name="validateStatus">当前任务状态，用于验证</param>
        /// <param name="isLogComplete">是否记录完成信息(如果任务状态为成功或者失败)</param>
        /// <param name="logLevel">日志级别</param>
        /// <param name="logMessage">日志</param>
        /// <param name="logFormatter">日志格式化器</param>
        /// <returns>信息是否有变化</returns>
        protected virtual async Task<bool> UpdateCoreAsync(Func<TaskCenterUpdatingContext, Task> func,
            TaskCenterRecordStatusEnum validateStatus = TaskCenterRecordStatusEnum.Excuting,
            bool isLogComplete = true,
            TaskCenterRecordLogTypeEnum logLevel = TaskCenterRecordLogTypeEnum.Business,
            string logMessage = null,
            Func<TaskCenterUpdatingContext, string> logFormatter = null)
        {
            CancellationToken.ThrowIfCancellationRequested();

            var hasChanges = false;//任务信息是否有改变
            TaskCenterUpdatingContext context = null;
            await UsingBlockingLockAsync(async () =>
            {
                using (var unitOfWork = UnitOfWorkManager.Begin(true))
                {
                    context = await LoadUpdatingContextAsync();
                    Validate(context, validateStatus);

                    await func(context);

                    if (context.HasChanges())
                    {
                        await UpdateRecordAsync(context); //更新任务信息
                    }

                    if (context.ExtensionHasChanges())
                    {
                        await UpdateExtensionAsync(context); //更新扩展信息
                    }

                    hasChanges = context.HasChanges() || context.ExtensionHasChanges();

                    if (hasChanges)
                    {
                        await unitOfWork.CompleteAsync(CancellationToken);

                        if (!IsDistributedTask)
                        {
                            _entity = context.Record;
                            _extension = (context as IExtensionGetter)?.Get() ?? _extension;
                        }
                    }
                }
            });

            await WriteLogAsync(context, hasChanges, logLevel, logMessage, logFormatter, isLogComplete);

            return hasChanges;
        }


        /// <summary>
        /// 更新任务信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual async Task UpdateRecordAsync(TaskCenterUpdatingContext context)
        {
            if (!IsDistributedTask && context.HasChanges())
            {
                CancellationToken.ThrowIfCancellationRequested();
                await RecordRepository.UpdateByPropertyNameAsync(context.Record, context.GetChanges().ToArray(), CancellationToken);
            }
        }

        /// <summary>
        /// 更新扩展信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual async Task UpdateExtensionAsync(TaskCenterUpdatingContext context)
        {
            if (context.ExtensionHasChanges() && context is IExtensionGetter getter)
            {
                var local = _extension = getter.Get();
                var taskExtension = new TaskCenterRecordExtensionEntity
                {
                    ID = TaskCenterRecordId,
                    ExcutingInfo = local.ExcutingInfo,
                    ExcuteResult = local.ExcuteResult,
                };

                await ExtensionRepository.UpdateByPropertyNameAsync(taskExtension, context.GetExtensionChangeProperties().ToArray(), CancellationToken);
            }
        }

        /// <summary>
        /// 写更新日志
        /// </summary>
        /// <param name="context">更新上下文</param>
        /// <param name="hasChanges">是否有修改</param>
        /// <param name="logLevel">日志基本</param>
        /// <param name="message">日志</param>
        /// <param name="logFormatter">日志格式化器</param>
        /// <param name="isLogComplete">是否记录完成信息(如果任务状态为成功或者失败)</param>
        /// <returns></returns>
        protected async Task WriteLogAsync(TaskCenterUpdatingContext context, bool hasChanges, TaskCenterRecordLogTypeEnum logLevel, string message, Func<TaskCenterUpdatingContext, string> logFormatter, bool isLogComplete)
        {
            var logs = new List<TaskCenterRecordLogDto>(2);
            if (hasChanges && (!string.IsNullOrEmpty(message) || logFormatter != null))
            {
                message = !string.IsNullOrEmpty(message) ? message : logFormatter(context);
                logs.Add(CreateLog(logLevel, message));
            }

            if (isLogComplete && (context.Status == TaskCenterRecordStatusEnum.ExcuteSuccess || context.Status == TaskCenterRecordStatusEnum.ExcuteFail))
            {
                var logmessage = context.Status == TaskCenterRecordStatusEnum.ExcuteSuccess ? "任务执行成功。" : "任务执行失败!";
                logs.Add(CreateLog(TaskCenterRecordLogTypeEnum.Business, logmessage));
            }

            if (logs.Any())
            {
                await LogService.InsertAsync(logs, CancellationToken);
            }
        }

        protected virtual async Task WriteLogAsync(TaskCenterRecordLogTypeEnum level, string message, string content = null, string remark = null)
        {
            CancellationToken.ThrowIfCancellationRequested();

            var log = CreateLog(level, message, content, remark);
            await LogService.PostTaskCenterRecordLogAsync(log, CancellationToken);
        }

        protected TaskCenterRecordLogDto CreateLog(TaskCenterRecordLogTypeEnum level, string message, string content = null, string remark = null)
        {
            if (!string.IsNullOrEmpty(message) && message.Length > 250)
            {
                message = message.Substring(0, 250);
            }

            var log = new TaskCenterRecordLogDto
            {
                TaskCenterRecordId = TaskCenterRecordId,
                Message = message,
                Content = content,
                Remark = remark,
                Type = level,
            };

            return log;
        }

        protected virtual Task UsingBlockingLockAsync(Func<Task> func)
        {
            if (!IsDistributedTask) return func();

            async Task createDistributeLockAsync()
            {
                var resource = TaskCenterHelper.GetUpdateTaskCenterRecordResource(this.TaskCenterRecordId);

                var tryCount = MAXTRYLOCKCOUNT;
                while (tryCount-- > 0)
                {
                    var redLock = await DistributeLocker.CreateBlockingLockAsync(resource, TimeSpan.FromSeconds(4), TimeSpan.FromSeconds(22), TimeSpan.FromMilliseconds(600), CancellationToken);
                    if (redLock.IsAcquired || tryCount == 0)
                    {
                        await redLock.UsingBlockingLockAsync(func);
                        break;
                    }
                    else
                    {
                        //redLock.Dispose();
                    }

                    await Task.Delay(50);
                };
            }

            return createDistributeLockAsync();
        }

        /// <summary>
        /// 根据属性名加载扩展信息
        /// </summary>
        /// <param name="propertyNames"></param>
        /// <returns></returns>
        protected async Task LoadExtensionByPropertyNameAsync(ExtensionInfoLocal extension, params string[] propertyNames)
        {
            CancellationToken.ThrowIfCancellationRequested();

            if (propertyNames.Length == 0) return;

            var resultName = TaskCenterUpdatingContext.EXCUTERESULT_PROPERTY_NAME;
            var infoName = TaskCenterUpdatingContext.EXCUTINGINFO_PROPERTY_NAME;

            if (!IsDistributedTask && _extension != null)
            {
                var names = new List<string>();
                if (propertyNames.Contains(resultName) && !extension.ResultIsLoaded)
                {
                    names.Add(resultName);
                }

                if (propertyNames.Contains(infoName) && !extension.ExcutingInfoIsLoaded)
                {
                    names.Add(infoName);
                }

                propertyNames = names.ToArray();

                if (!propertyNames.Any())
                {
                    return;
                }

            }

            var model = await ExtensionRepository.GetByPropertyNameAsync(TaskCenterRecordId, propertyNames, cancellationToken: CancellationToken);

            if (propertyNames.Contains(resultName))
            {
                extension.ExcuteResult = model.ExcuteResult;
                extension.ResultIsLoaded = true;
            }

            if (propertyNames.Contains(infoName))
            {
                extension.ExcutingInfo = model.ExcutingInfo;
                extension.ExcutingInfoIsLoaded = true;
            }

        }

        /// <summary>
        /// 验证
        /// </summary>
        /// <param name="context">taskCenterRecord</param>
        /// <param name="status">taskCenterRecordStatusEnum</param>
        protected virtual void Validate(TaskCenterUpdatingContext context, TaskCenterRecordStatusEnum status = TaskCenterRecordStatusEnum.Excuting)
        {
            if (context.Status != status)
            {
                throw new TaskCenterValidateException(CommonExceptionCode.TaskCenterValidateStatusNotEqual,
                    $"{TaskCenterRecordId}请确认执行状态，Now:{context.Status.GetDescription()},Verify:{status.GetDescription()}");
            }

            if (context.RetryCount != CurrentRetryCount)
            {
                throw new TaskCenterValidateException(CommonExceptionCode.TaskCenterValidateRetryCountNotEqual,
                    $"{TaskCenterRecordId}请确认重试次数，Now:{context.RetryCount},Verify:{CurrentRetryCount}");
            }
        }

        /// <summary>
        /// 加载任务更新上下文
        /// </summary>
        /// <returns></returns>
        protected async Task<TaskCenterUpdatingContext> LoadUpdatingContextAsync()
        {
            if (IsDistributedTask || _entity == null)//分布式任务，每次都需要重新获取
            {
                _entity = await RecordRepository.FindAsync(this.TaskCenterRecordId, CancellationToken);
            }

            if (IsDistributedTask || _extension == null) //分布式任务，每次都需要重新获取
            {
                _extension = new ExtensionInfoLocal();
            }

            var entity = IsDistributedTask ? _entity : ObjectMapper.Map<TaskCenterRecordEntity, TaskCenterRecordEntity>(_entity); //复制一个对象，用于更新

            var extension = IsDistributedTask ? _extension : ObjectMapper.Map<ExtensionInfoLocal, ExtensionInfoLocal>(_extension);

            var context = new InternalTaskCenterUpdatingContext(entity, extension, LoadExtensionByPropertyNameAsync);

            return context;
        }

        /// <summary>
        /// 更新开始信息
        /// </summary>
        /// <param name="entity">entity</param>
        protected void UpdateStartInfo(TaskCenterUpdatingContext entity)
        {
            entity.Status = TaskCenterRecordStatusEnum.Excuting;
            entity.ExcuteBeginDatetime = DateTime.Now;
            entity.IsTimeOut = false;
            entity.ExcutingProgress = 0;
            entity.ResultMessage = null;
        }

        protected TModel LoadModel<TModel>(TaskCenterUpdatingContext context, TModel model)
            where TModel : TaskCenterRecordProgressModel
        {
            model.ResultMessage = context.ResultMessage;
            model.ExcutingProgress = context.ExcutingProgress;

            return model;
        }

        /// <summary>
        /// 设置<paramref name="context"/>的属性值，如果<paramref name="isAutoComplete"/>为true，返回是否已完成标记
        /// </summary>
        /// <param name="context">任务更新上下文</param>
        /// <param name="model">任务进度信息</param>
        /// <param name="isAutoComplete">是否自动推断任务是否已完成</param>
        /// <returns>是否已完成</returns>
        protected bool StoreContext(TaskCenterUpdatingContext context, TaskCenterRecordProgressModel model, bool isAutoComplete)
        {
            context.ResultMessage = model.ResultMessage;
            context.ExcutingProgress = model.ExcutingProgress;

            var isCompleted = false;
            if (isAutoComplete && context.ExcutingProgress >= 100)
            {
                context.ExcutingProgress = 100;
                context.ExcuteEndDatetime = DateTime.Now;
                context.Status = TaskCenterRecordStatusEnum.ExcuteSuccess;

                isCompleted = true;
            }

            if (!isAutoComplete && context.ExcutingProgress >= 100)
            {
                context.ExcutingProgress = 98;
            }

            return isCompleted;
        }

        protected virtual string Serialize(object obj)
        {
            if (obj == null) return null;

            if (IsSimpleType(obj.GetType()))
            {
                return obj.ToString();
            }

            return JsonSerializer.Serialize(obj, false, false);
        }

        protected virtual T Deserialize<T>(string jsonString)
        {
            if (jsonString == null) return default(T);

            if (IsSimpleType(typeof(T)))
            {
                return (T)Convert.ChangeType(jsonString, typeof(T));
            }
            return JsonSerializer.Deserialize<T>(jsonString, false);
        }

        /// <summary>
        /// 验证是否简单类型
        /// </summary>
        /// <param name="type">type</param>
        /// <returns></returns>
        protected bool IsSimpleType(Type type)
        {
            return type.IsPrimitive
                    || type.IsEnum
                    || type.Equals(typeof(string))
                    || type.Equals(typeof(decimal));
        }
        #endregion //end protected methods

        #region inner class

        /// <summary>
        /// 获取扩展信息接口
        /// </summary>
        protected interface IExtensionGetter
        {
            ExtensionInfoLocal Get();
        }

        /// <summary>
        /// 任务中心上下文
        /// </summary>
        public class InternalTaskCenterUpdatingContext : TaskCenterUpdatingContext, IExtensionGetter
        {
            public InternalTaskCenterUpdatingContext(TaskCenterRecordEntity entity,
                ExtensionInfoLocal extension = null,
                Func<ExtensionInfoLocal, string[], Task> extensionFacotry = null)
                : base(entity, extension, extensionFacotry)
            {
            }

            ExtensionInfoLocal IExtensionGetter.Get()
            {
                return Extension;
            }
        }

        #endregion //end inner class
    }
}
